package top.zhangjianyong.tools.indicator.strategy.impl;

import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.indicator.context.IndicatorContext;
import top.zhangjianyong.tools.indicator.model.TechnicalIndicatorResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * MACD（指数平滑异同移动平均线）指标计算器
 * 参数：fast=12, slow=26, signal=9
 *
 * @author zhangjianyong
 * @date 2024/01/01
 */
@Component
public class MACDIndicator extends AbstractIndicator {

    private static final String INDICATOR_NAME = "MACD";
    private static final int FAST_PERIOD = 12;
    private static final int SLOW_PERIOD = 26;
    private static final int SIGNAL_PERIOD = 9;

    @Override
    public String getName() {
        return INDICATOR_NAME;
    }

    @Override
    public int getMinDataSize() {
        return SLOW_PERIOD + SIGNAL_PERIOD; // 至少需要35个数据点
    }

    @Override
    protected TechnicalIndicatorResult doCalculate(IndicatorContext context) {
        List<ETFData> dataList = context.getDataList();

        // 计算快线EMA12和慢线EMA26
        BigDecimal ema12 = calculateEMA(dataList, FAST_PERIOD);
        BigDecimal ema26 = calculateEMA(dataList, SLOW_PERIOD);

        if (ema12 == null || ema26 == null) {
            return TechnicalIndicatorResult.failure(getName(), "无法计算EMA12或EMA26");
        }

        // DIF = EMA12 - EMA26
        BigDecimal dif = ema12.subtract(ema26);

        // 计算DEA（DIF的9日EMA）
        // 需要先计算DIF序列，然后对DIF序列计算EMA9
        BigDecimal dea = calculateDEA(dataList, dif);

        // MACD = (DIF - DEA) * 2
        BigDecimal macd = dif.subtract(dea).multiply(new BigDecimal(2));

        TechnicalIndicatorResult result = TechnicalIndicatorResult.success(getName());
        result.addValue("macd_dif", dif.setScale(4, RoundingMode.HALF_UP));
        result.addValue("macd_dea", dea.setScale(4, RoundingMode.HALF_UP));
        result.addValue("macd", macd.setScale(4, RoundingMode.HALF_UP));

        return result;
    }

    /**
     * 计算EMA
     */
    private BigDecimal calculateEMA(List<ETFData> dataList, int period) {
        if (dataList.size() < period) {
            return null;
        }

        BigDecimal k = new BigDecimal(2)
            .divide(new BigDecimal(period + 1), 8, RoundingMode.HALF_UP);
        BigDecimal oneMinusK = BigDecimal.ONE.subtract(k);

        BigDecimal ema = dataList.get(0).getValue();
        for (int i = 1; i < dataList.size(); i++) {
            BigDecimal price = dataList.get(i).getValue();
            ema = price.multiply(k).add(ema.multiply(oneMinusK));
        }

        return ema.setScale(4, RoundingMode.HALF_UP);
    }

    /**
     * 计算DEA（DIF的9日EMA）
     * 简化处理：使用当前DIF值作为基础，结合历史DIF值计算
     */
    private BigDecimal calculateDEA(List<ETFData> dataList, BigDecimal currentDif) {
        // 简化实现：计算历史DIF序列的EMA9
        // 由于需要历史DIF值，这里使用简化方法
        // 实际应用中需要维护DIF序列

        // 计算历史DIF值（简化：使用价格差值近似）
        if (dataList.size() < SLOW_PERIOD + SIGNAL_PERIOD) {
            return BigDecimal.ZERO;
        }

        // 计算最近SIGNAL_PERIOD个DIF值的EMA
        BigDecimal k = new BigDecimal(2)
            .divide(new BigDecimal(SIGNAL_PERIOD + 1), 8, RoundingMode.HALF_UP);
        BigDecimal oneMinusK = BigDecimal.ONE.subtract(k);

        // 计算初始DIF（使用前SIGNAL_PERIOD个数据点）
        BigDecimal initialDif = BigDecimal.ZERO;
        for (int i = SLOW_PERIOD; i < SLOW_PERIOD + SIGNAL_PERIOD && i < dataList.size(); i++) {
            BigDecimal ema12 = calculateEMA(dataList.subList(0, i + 1), FAST_PERIOD);
            BigDecimal ema26 = calculateEMA(dataList.subList(0, i + 1), SLOW_PERIOD);
            if (ema12 != null && ema26 != null) {
                initialDif = ema12.subtract(ema26);
            }
        }

        // 使用当前DIF值更新DEA
        BigDecimal dea = initialDif;
        if (dea.compareTo(BigDecimal.ZERO) == 0) {
            dea = currentDif;
        } else {
            dea = currentDif.multiply(k).add(dea.multiply(oneMinusK));
        }

        return dea.setScale(4, RoundingMode.HALF_UP);
    }
}

